// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Vector2Test.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace Test.Robotics.Numerics
{
    using System;

    using Microsoft.Robotics.Numerics;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Vector2 unit tests
    /// </summary>
    [TestClass]
    public class Vector2Test
    {
        /// <summary>
        /// Gets or sets vector2 property
        /// </summary>
        public Vector2 VectorProperty { get; set; }

        /// <summary>
        /// Tests the default constructor
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2DefaultConstructor()
        {
            Vector2 v = new Vector2();
            Assert.IsTrue(
                v.X == 0.0 && v.Y == 0.0, "Vector2 Components are not zero in default constructor");
        }

        /// <summary>
        /// Tests the constructor with doubles
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2DoubleValuesConstructor()
        {
            Vector2 v = new Vector2(1.0, 2.0);
            Assert.IsTrue(
                v.X == 1.0 && v.Y == 2.0, "Vector2 Components are not what was passed into constructor");
        }

        /// <summary>
        /// Tests the copy constructor
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2CopyConstructor()
        {
            Vector2 initialVector = new Vector2(1.0, 2.0);
            Vector2 v = new Vector2(initialVector);
            Assert.IsTrue(v.X == 1.0 && v.Y == 2.0, "Vector2 copy constructor failed");
        }

        /// <summary>
        /// Test vector axis methods
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2VectorAxis()
        {
            Vector2 v = Vector2.XAxis;
            Assert.IsTrue(v.X == 1.0 && v.Y == 0.0, "Vector2 XAxis failed");

            v = Vector2.NegativeXAxis;
            Assert.IsTrue(v.X == -1.0 && v.Y == 0.0, "Vector2 NegativeXAxis failed");

            v = Vector2.YAxis;
            Assert.IsTrue(v.X == 0.0 && v.Y == 1.0, "Vector2 YAxis failed");

            v = Vector2.NegativeYAxis;
            Assert.IsTrue(v.X == 0.0 && v.Y == -1.0, "Vector2 NegativeYAxis failed");
        }

        /// <summary>
        /// Tests the minus operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2MinusOperator()
        {
            Vector2 initialVector = new Vector2(1.0, 2.0);
            Vector2 v = -initialVector;
            Assert.IsTrue(
                (Math.Abs(v.X + initialVector.X) < MathConstants.ErrorEpsilon)
                && (Math.Abs(v.Y + initialVector.Y) < MathConstants.ErrorEpsilon),
                "Vector2 minus operator failed");
        }

        /// <summary>
        /// Tests the addition operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2AdditionOperator()
        {
            Vector2 leftSide = new Vector2(10.0, 20.0);
            Vector2 rightSide = new Vector2(1.0, 2.0);
            Vector2 result = leftSide + rightSide;
            Assert.IsTrue(
                (Math.Abs(result.X - 11.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 22.0) < MathConstants.ErrorEpsilon),
                "Vector2 addition operator failed");
        }

        /// <summary>
        /// Tests the subtraction operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2SubtractionOperator()
        {
            Vector2 leftSide = new Vector2(10.0, 20.0);
            Vector2 rightSide = new Vector2(1.0, 2.0);
            Vector2 result = leftSide - rightSide;
            Assert.IsTrue(
                (Math.Abs(result.X - 9.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 18.0) < MathConstants.ErrorEpsilon),
                "Vector2 subtraction operator failed");
        }

        /// <summary>
        /// Tests scalar divide
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2ScalarDivide()
        {
            Vector2 initialVector = new Vector2(10.0, 20.0);
            Vector2 result = initialVector / 2.0;
            Assert.IsTrue(
                (Math.Abs(result.X - 5.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 10.0) < MathConstants.ErrorEpsilon),
                "Vector2 scalar divide operator failed");
        }

        /// <summary>
        /// Tests scalar multiply
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2ScalarMultiply()
        {
            Vector2 initialVector = new Vector2(10.0, 20.0);
            Vector2 result = initialVector * 2.0;
            Assert.IsTrue(
                Math.Abs(result.X - 20.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.Y - 40.0) < MathConstants.ErrorEpsilon,
                "Vector2 scalar multiply operator from right failed");
            result = 2.0 * initialVector;
            Assert.IsTrue(
                Math.Abs(result.X - 20.0) < MathConstants.ErrorEpsilon
                && Math.Abs(result.Y - 40.0) < MathConstants.ErrorEpsilon,
                "Vector2 scalar multiply operator from left failed");
        }

        /// <summary>
        /// Tests getting vector L2 norm
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2VectorL2Norm()
        {
            Vector2 initialVector = new Vector2(1.0, 2.0);
            double result = initialVector.L2Norm();
            Assert.IsTrue(
                Math.Abs(result - Math.Sqrt(5.0)) < MathConstants.ErrorEpsilon, "Vector2 L2 norm calculation failed");
        }

        /// <summary>
        /// Tests getting vector Length
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2VectorLength()
        {
            Vector2 initialVector = new Vector2(1.0, 2.0);
            double result = initialVector.Length();
            Assert.IsTrue(
                Math.Abs(result - Math.Sqrt(5.0)) < MathConstants.ErrorEpsilon, "Vector2 L2 norm calculation failed");
        }

        /// <summary>
        /// Tests vector normalization
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2VectorNormalization()
        {
            Vector2 result;
            result = Vector2.Normalize(new Vector2(2.0, 0.0));
            Assert.IsTrue(
                (Math.Abs(result.X - 1.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 0.0) < MathConstants.ErrorEpsilon),
                "Vector2 normalize X component failed");

            result = Vector2.Normalize(new Vector2(0.0, 2.0));
            Assert.IsTrue(
                (Math.Abs(result.X - 0.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 1.0) < MathConstants.ErrorEpsilon),
                "Vector2 normalize Y component failed");

            result = Vector2.Normalize(new Vector2(0.0, 0.0));
            Assert.IsTrue(
                (Math.Abs(result.X - 0.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 0.0) < MathConstants.ErrorEpsilon),
                "Vector2 normalize Z component failed");

            result = Vector2.Normalize(new Vector2(0.0, 0.0));
            Assert.IsTrue(
                (Math.Abs(result.X - 0.0) < MathConstants.ErrorEpsilon)
                && (Math.Abs(result.Y - 0.0) < MathConstants.ErrorEpsilon),
                "Vector2 normalize zero length failed");
        }

        /// <summary>
        /// Tests vector normalization
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2Normalization()
        {
            Vector2 vec = new Vector2(1, -1);

            vec = Vector2.Normalize(vec);

            Assert.AreEqual(1, vec.Length(), MathConstants.ErrorEpsilon);
        }

        /// <summary>
        /// Tests vector dot product
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2VectorDotProduct()
        {
            Vector2 left = new Vector2(1.0, 2.0);
            Vector2 right = new Vector2(10.0, 20.0);
            double result = Vector2.Dot(left, right);
            Assert.IsTrue(Math.Abs(result - 50.0) < MathConstants.ErrorEpsilon, "Vector2 Dot product failed");
        }

        /// <summary>
        /// Tests the Hashcode method
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2GetHashCode()
        {
            Vector2 vector = new Vector2();
            int result = vector.GetHashCode();
            Assert.IsTrue(result != 0, "Vector2 hash code is zero");
        }

        /// <summary>
        /// Tests the equality operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2EqualityOperator()
        {
            Vector2 vector1 = new Vector2(1.0, 2.0);
            Vector2 vector2 = new Vector2(1.0, 2.0);
            Assert.IsTrue(vector1 == vector2, "Vector2 should have been equal");
            vector2.X = 2.0;
            Assert.IsFalse(vector1 == vector2, "Vector2 should have been not equal in X component");
            vector2.X = 1.0;
            vector2.Y = 1.0;
            Assert.IsFalse(vector1 == vector2, "Vector2 should have been not equal in Y component");
            Assert.IsTrue(vector1 != vector2, "Vector2 != operator failed");
        }

        /// <summary>
        /// Tests the overridden equals method
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2EqualityMethod()
        {
            Vector2 vector1 = new Vector2(1.0, 2.0);
            Vector2 vector2 = new Vector2(1.0, 2.0);
            Assert.IsTrue(vector1.Equals(vector2), "Vector2 equals method failed");
            int foo = 0;
            Assert.IsFalse(vector1.Equals(foo), "Vector2 equals method retuned wrong result for unlike objects");
            vector2.X = 2.0;
            Assert.IsFalse(vector1.Equals(vector2), "Vector2 equals method failed in X component");
            vector2.X = 1.0;
            vector2.Y = 1.0;
            Assert.IsFalse(vector1.Equals(vector2), "Vector2 equals method failed in Y component");
        }

        /// <summary>
        /// Test the ToString operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2ToString()
        {
            Vector2 vector = new Vector2(1.0, 2.0);
            string str = vector.ToString();

            // Not sure what the test should be
            Assert.IsTrue(str != null, "Vector2 tostring failed");
        }

        /// <summary>
        /// Test the DistanceFrom operator
        /// </summary>
        [Priority(0)]
        [TestMethod]
        [TestCategory("Unit")]
        public void Vector2DistanceFrom()
        {
            Vector2 vector1 = new Vector2(2, 3);
            Vector2 vector2 = new Vector2(1, 1);

            Assert.IsTrue(
                Math.Abs(Math.Sqrt(5) - vector1.DistanceFrom(vector2)) < MathConstants.ErrorEpsilon,
                "Distance is correct");
        }
    }
}
